// Copyright 2022 Matrix Origin
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package table

import (
	"reflect"
	"testing"
	"time"

	"github.com/prashantv/gostub"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/matrixorigin/matrixone/pkg/common/util"
)

func init() {
	// Tips: Op 'time.Local = time.FixedZone(...)' would cause DATA RACE against to time.Now()
}

func TestPathBuilder(t *testing.T) {
	type field struct {
		builder PathBuilder
	}
	type args struct {
		account  string
		typ      MergeLogType
		ts       time.Time
		db       string
		name     string
		nodeUUID string
		nodeType string
		ext      string
	}
	tests := []struct {
		name        string
		field       field
		args        args
		wantDir     string
		wantETLPath string
		wantLogFN   string
	}{
		{
			name:  "db_tbl",
			field: field{builder: NewDBTablePathBuilder()},
			args: args{
				account:  "user",
				typ:      MergeLogTypeLogs,
				ts:       time.Unix(0, 0).UTC(),
				db:       "db",
				name:     "table",
				nodeUUID: "123456",
				nodeType: "node",
				ext:      CsvExtension,
			},
			wantDir:     `db`,
			wantETLPath: `db/table_*.csv`,
			wantLogFN:   `table_123456_node_19700101.000000.000000.csv`,
		},
		{
			name:  "db_tbl.tae",
			field: field{builder: NewDBTablePathBuilder()},
			args: args{
				account:  "user",
				typ:      MergeLogTypeLogs,
				ts:       time.Unix(0, 0).UTC(),
				db:       "db",
				name:     "table",
				nodeUUID: "123456",
				nodeType: "node",
				ext:      TaeExtension,
			},
			wantDir:     `db`,
			wantETLPath: `db/table_*.csv`,
			wantLogFN:   `table_123456_node_19700101.000000.000000.tae`,
		},
		{
			name:  "account_date",
			field: field{builder: NewAccountDatePathBuilder()},
			args: args{
				account:  "user",
				typ:      MergeLogTypeLogs,
				ts:       time.Unix(0, 0),
				db:       "db",
				name:     "table",
				nodeUUID: "123456",
				nodeType: "node",
				ext:      CsvExtension,
			},
			wantDir:     `user/logs` + `/1970/01/01` + `/table`,
			wantETLPath: `/*/*` + `/*/*/*` + `/table/*`,
			wantLogFN:   `0_123456_node_0.csv`,
		},
		{
			name:  "account_date.tae",
			field: field{builder: NewAccountDatePathBuilder()},
			args: args{
				account:  "user",
				typ:      MergeLogTypeLogs,
				ts:       time.Unix(0, 0),
				db:       "db",
				name:     "table",
				nodeUUID: "123456",
				nodeType: "node",
				ext:      TaeExtension,
			},
			wantDir:     `user/logs` + `/1970/01/01` + `/table`,
			wantETLPath: `/*/*` + `/*/*/*` + `/table/*`,
			wantLogFN:   `0_123456_node_0.tae`,
		},
	}

	ssStub := gostub.Stub(&NSecString, func() string { return "0" })
	defer ssStub.Reset()

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			m := tt.field.builder
			gotDir := m.Build(tt.args.account, tt.args.typ, tt.args.ts, tt.args.db, tt.args.name)
			require.Equal(t, tt.wantDir, gotDir)
			gotETLPath := m.BuildETLPath(tt.args.db, tt.args.name, ETLParamAccountAll)
			require.Equal(t, tt.wantETLPath, gotETLPath)
			gotLogFN := m.NewLogFilename(tt.args.name, tt.args.nodeUUID, tt.args.nodeType, tt.args.ts, tt.args.ext)
			require.Equal(t, tt.wantLogFN, gotLogFN)
		})
	}
}

func TestString2Bytes(t *testing.T) {
	type args struct {
		s string
	}
	tests := []struct {
		name    string
		args    args
		wantRet []byte
	}{
		{
			name: "normal",
			args: args{
				s: "12345a",
			},
			wantRet: []byte{'1', '2', '3', '4', '5', 'a'},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if gotRet := util.UnsafeStringToBytes(tt.args.s); !reflect.DeepEqual(gotRet, tt.wantRet) {
				t.Errorf("String2Bytes() = %v, want %v", gotRet, tt.wantRet)
			}
		})
	}
}

func TestGetExtension(t *testing.T) {
	type args struct {
		ext string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: CsvExtension,
			args: args{ext: CsvExtension},
			want: ".csv",
		},
		{
			name: TaeExtension,
			args: args{ext: TaeExtension},
			want: ".tae",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equalf(t, tt.want, GetExtension(tt.args.ext), "GetExtension(%v)", tt.args.ext)
		})
	}
}
